#pragma  once
#include "ImagePos.h"
#include "ImageWrapper.h"

class CImageWrapper;

//=============================================================================
// Integral Image:
//      Suppose input image is raw(x,y), integralimage is integral(x,y)
//      integral(i,j)=sum( raw( x<=i, y<=j ) )
//=============================================================================

class CIntegralImageBase
{
protected:
    unsigned  m_uWidth;
    unsigned  m_uHeight;
	unsigned  m_uMemoryAllocated;

    int *m_piItgImage;

    HRESULT AllocMemory(unsigned uWidth, unsigned uHeight);
    void ReleaseMemory();

public:
	virtual ~CIntegralImageBase();
	CIntegralImageBase() : m_piItgImage(NULL), m_uWidth(0), m_uHeight(0), m_uMemoryAllocated(0) {}


	inline unsigned GetImageWidth() const { return m_uWidth; }
    inline const int GetImageHeight() const { return m_uHeight; }
    inline const int *GetData() const  {   return (const int *)m_piItgImage;    }
	template<class CImagePosType>    HRESULT CalCIntegralImage(const CImageWrapper *pImage, CImagePosType& imagePos);

	HRESULT CalCIntegralImage(const CImageWrapper* pImage);
	HRESULT CalCIntegralImage(const CImageWrapper* pImage, unsigned uDstWidth, unsigned uDstHeight);
	HRESULT CalCIntegralImage(const CImageWrapper* pImage, unsigned uDstWidth, unsigned uDstHeight, double dAngle);
	HRESULT CalCIntegralImage(const BYTE* pBuf, unsigned uSrcWidth, unsigned uSrcHeight);

};

class CIntegralImage : public CIntegralImageBase
{
protected:
	float m_flAutoContrast;
	float m_flAutoContrastFactor;

	HRESULT AutoContrast(const unsigned int* puGrayHist);
public:
	CIntegralImage () : m_flAutoContrastFactor(1.0f), m_flAutoContrast(1.0f) {}
	~CIntegralImage(){}
	void SetAutoContrastFactor(float flAutoContrastFactor) { m_flAutoContrastFactor = flAutoContrastFactor; }
	inline const float GetAutoContrast() const { return m_flAutoContrast; }
	template<class CImagePosType> HRESULT CalCIntegralImage(const CImageWrapper *pImage, CImagePosType& imagePos );

};

//===========================================================================
//    CalCIntegralImage
//        Calculate integral image of the input image
//	  Algorithm description
//			Initialize ii(-1,.) = 0, ii(.,-1)=0
//			for (x=0; x< width ; x++)
//				for (y=0; y< height; y++ )
//					s(x,y) = s(x,y-1) + i(x,y)
//					ii(x,y) = ii(x-1,y) + s(x,y)
//    Parameters     
//        pImage
//            Input image
//        nDstWidth, nDstHeight
//            rescale the input image to this size, 
//            then calculate the integralimage 
//
//    Return Values
//        S_OK indicates success. Otherwise for failure
//===========================================================================
template<class CImagePosType>    
inline HRESULT CIntegralImageBase::CalCIntegralImage(const CImageWrapper *pImage, CImagePosType& imagePos )
{
	HRESULT hr;

	//m_uWidth, m_uHeight are equal to the desired image size after allocation successfully
	hr = AllocMemory(imagePos.GetDstWidth(), imagePos.GetDstHeight());

	if (SUCCEEDED(hr))
	{
		unsigned uIntegralStride = m_uWidth + 1;
		for (unsigned x = 0; x< m_uWidth; x++)
		{
			imagePos.InitIndex(x,0);
			unsigned index_xy = x+1;			//PointTo ii(x,-1)
			int iSumCol=0;		// s(x,-1)=0
			int iGray;
			for (unsigned y = 0; y < m_uHeight; y++, imagePos.IncIndexY())
			{
				iGray = pImage->GetGrayPixel(imagePos.GetX(), imagePos.GetY());
				iSumCol += iGray;					//calculate s(x,y)
				index_xy += uIntegralStride;		//Point to ii(x,y)
				m_piItgImage[index_xy] = m_piItgImage[index_xy-1] + iSumCol;
			}
		}
	}
	return hr;
}

template<class CImagePosType>    
inline HRESULT CIntegralImage::CalCIntegralImage(const CImageWrapper *pImage, CImagePosType& imagePos )
{
	HRESULT hr;

	//m_uWidth, m_uHeight are equal to the desired image size after allocation successfully
	hr = AllocMemory(imagePos.GetDstWidth(), imagePos.GetDstHeight());
	unsigned int puGrayHist[256];
	memset(puGrayHist, 0, 256*sizeof(int) );

	if (SUCCEEDED(hr))
	{
		unsigned uIntegralStride = m_uWidth + 1;
		for (unsigned x = 0; x< m_uWidth; x++)
		{
			imagePos.InitIndex(x,0);
			unsigned index_xy = x+1;			//PointTo ii(x,-1)
			int iSumCol=0;		// s(x,-1)=0
			int iGray;
			for (unsigned y = 0; y < m_uHeight; y++, imagePos.IncIndexY())
			{
				iGray = pImage->GetGrayPixel(imagePos.GetX(), imagePos.GetY());
				iSumCol += iGray;					//calculate s(x,y)
				puGrayHist[iGray]++;
				index_xy += uIntegralStride;		//Point to ii(x,y)
				m_piItgImage[index_xy] = m_piItgImage[index_xy-1] + iSumCol;
			}
		}
		hr = AutoContrast(puGrayHist);
	}
	return hr;
}


